<?php
/**
 * 上传类
 * @author：upall
 * @package Lib
 * @version	12:07 2011年1月24日 星期一
 */

if(!defined('EASYMVC')) {
	exit('Access Denied');
}

/**
 * 文件普通上传 
 */
class Lib_Upload{
   public $uploadFolder = 'upload'; // 图片存放目录(末尾不要加"/")
   public $thumbFolder = 'upload/thumb'; // 缩略图(末尾不要加"/")
   public $autoSubfolder = TRUE; // 是否自动创建下级目录
   public $SubfolderFormat = 'Y-m'; // 子目录格式（使用DATE函数的格式）
   public $uploadFileType = 'file'; // 上传类型：file或image
   public $fileName ='';
   // 如果上传类型是：file
   public $denyFileType = 'asp,php,aspx,jsp,html,js,vbs'; // 拒绝上传的扩展名（不是文件类型！）
   // 如果上传类型是：imgae
   public $autoThumb = TRUE; // 是否自动生成缩略图
   public $thumbWidth = 160; // 缩略图最大宽度
   public $thumbHeight = 120; // 缩略图最大高度
   public $autoShandow = FALSE; // 是否自动对缩略图生成阴影
   public $shandowX = 5; // 投影相对于原图的左边距
   public $shandowY = 5; // 投影相对于原图的底边距
   public $shandowLength = 3; // 投影长度
   public $shondowColor = 'CCC'; // 四种投影颜色格式：#1FCC2B、#EEE、1FCC2B、EEE
   // 上传结果
   public $error = ''; // 错误信息
   public $imgPath = ''; // 上传成功后的图片位置
   public $thumbPath = ''; // 上传成功后的缩略图位置
   public $filePath = ''; // 上传成功后的文件位置

   public static function getInstance(){
	   static $obj;
	   if(!$obj){
			$obj = new self();
		}
	    return $obj;
   }
 
   // 说明：初始化
   function __construct($uploadFileType = '', $uploadFolder = '', $thumbFolder = ''){
      empty($uploadFolder) ? '' : $this->uploadFolder = $uploadFolder;
      empty($thumbFolder) ? '' : $this->thumbFolder = $thumbFolder;
      empty($uploadFileType) ? '' : $this->uploadFileType = $uploadFileType;
      if ('file' != $this->uploadFileType && 'image' != $this->uploadFileType){
         $this->uploadFileType = 'file';
      }else{
         $this->uploadFileType = strtolower($this->uploadFileType);
      }
   }
 
   // 说明：上传图片，参数是<input />的name属性值；成功返回图片的相对URL，失败返回FALSE和错误信息（在$this->error里）
   // bool/sting upload(string $html_tags_input_attrib_name);
   function save($inputName){ // 上传操作，参数是input标签的name属性。
      $this->_mkdir(); // 创建存放目录
      if ($this->error){ // 如果有错，直接返回（例如_mkdir）
         return FALSE;
      }
      if(!$_FILES[$inputName]["name"]){
         $this->error = '没有上传文件';
         return FALSE;
      }
      if($_FILES[$inputName]["name"]){
         $isUpFile = $_FILES[$inputName]['tmp_name'];
         if (is_uploaded_file($isUpFile)){
            if ('image' == $this->uploadFileType){
               // 图片上传
               $imgInfo = $this->_getinfo($isUpFile);
               if (FALSE == $imgInfo){
                  return FALSE;
               }
               $extName = $imgInfo['type'];
               $microSenond = floor(microtime()*10000);// 取一个毫秒级数字,4位。
               $newFileName = $uploadFolder . '/' . date('YmdHis') . $microSenond . '.' . $extName ; // 所上传图片的新名字。
               $location = $this->uploadFolder . $newFileName;
               $result = move_uploaded_file($isUpFile, $location);
               if ($result){
                  if (TRUE == $this->autoThumb){ // 是否生成缩略图
                     $thumb = $this->thumb($location, $this->thumbWidth, $this->thumbHeight);
                     if (FALSE == $thumb){
                        return FALSE;
                     }
                  }
                  $this->imgPath = $location;
                  return $location;
               }else{
                  $this->error = '移动临时文件时出错';
                  return FALSE;
               }
            }else if ('file' == $this->uploadFileType){
               // 文件上传
               $extName = $this->_checkType($_FILES[$inputName]["name"]);
               if (FALSE == $extName){
                  return FALSE;
               }
               $microSenond = floor(microtime()*10000);// 取一个毫秒级数字,4位。
               $newFileName = $uploadFolder . '/' . date('YmdHis') . $microSenond . '.' . $extName ; // 所上传文件的新名字。
               $location = $this->uploadFolder . $newFileName;
               $result = @move_uploaded_file($isUpFile, $location);
               if ($result){
				  return date($this->SubfolderFormat).$newFileName;
                  $this->filePath = $location;
                  return $location;
               }else{
                  $this->error = '移动临时文件时出错';
                  return FALSE;
               }
            }else{
               $this->error = '未知错误:文件类型';
               return FALSE;
            }
         }else{
            $uploadError = $_FILES[$inputName]['error'];
            if (1 == $uploadError){ // 文件大小超过了php.ini中的upload_max_filesize
               $this->error = '文件太大，服务器拒绝接收大于' . ini_get('upload_max_filesize') . '的文件';
               return FALSE;
            }elseif (3 == $uploadError){ // 上传了部分文件
               $this->error = '上传中断，请重试';
               return FALSE;
            }elseif (4 == $uploadError){
               $this->error = '没有文件被上传';
               return FALSE;
            }elseif (6 == $uploadError){
               $this->error = '找不到临时文件夹，请联系您的服务器管理员';
               return FALSE;
            }elseif (7 == $uploadError){
               $this->error = '文件写入失败，请联系您的服务器管理员';
               return FALSE;
            }else{
               if (0 != $uploadError){
                  $this->error = '未知上传错误，请联系您的服务器管理员';
                  return FALSE;
               }
            }
         }
      }
 
   }
 
   // 说明：获取图片信息，参数是上传后的临时文件，成功返回数组，失败返回FALSE和错误信息
   // array/bool _getinfo(string $upload_tmp_file)
   private function _getinfo($img){
      $tempfile = @fopen($img, "rb");
      $bin = fread($tempfile, 2); //只读2字节
      fclose($tempfile);
      $strInfo = @unpack("C2chars", $bin);
      $typeCode = intval($strInfo['chars1'] . $strInfo['chars2']);
      $fileType = '';
      switch ($typeCode){ // 6677:bmp 255216:jpg 7173:gif 13780:png 7790:exe 8297:rar 8075:zip tar:109121 7z:55122 gz 31139
         case '255216':
            $fileType = 'jpg';
            break;
         case '7173':
            $fileType = 'gif';
            break;
         case '13780':
            $fileType = 'png';
            break;
         default:
            $fileType = 'unknown';
      }
      if ($fileType == 'jpg' || $fileType == 'gif' || $fileType == 'png'){
         $imageInfo = getimagesize($img);
         $imgInfo['size'] = $imageInfo['bits'];
         $imgInfo["type"] = $fileType;
         $imgInfo["width"] = $imageInfo[0];
         $imgInfo["height"] = $imageInfo[1];
         return $imgInfo;
      }else{ // 非图片类文件信息
         $this->error = '图片类型错误';
         return FALSE;
      }
   } // end _getinfo
 
   // 检测扩展名是否允许（因未知扩展名太多，故不严格检测（只据扩展名，不据文件头））
   private function _checkType($file){
      $extName = preg_replace('/.*\.(.*[^\.].*)*/iU','\\1', $file);
      if (empty($extName)){
         $extName = 'unKnown';
      }
      $denyTypes = strtolower($this->denyFileType); // 先转成小写
      $denyTypes = explode(',', $this->denyFileType); // 再分解数组
      foreach ($denyTypes as $ext){
         $ext = strtolower($ext);
         if ($extName == $ext){
            $this->error = '不允许上传的文件类型：' . $extName;
            return FALSE;
         }
      }
      if (!$this->error){
         return $extName;
      }
   }
 
   // 说明：生成缩略图，等比例缩放或拉伸
   // bool/string thumb(string $uploaded_file, int $thumbWidth, int $thumbHeight, string $thumbTail);
   function thumb($img, $thumbWidth = '', $thumbHeight = '', $thumbTail = '_thumb'){
      $filename = $img; // 保留一个名字供新的缩略图名字使用
      $imgInfo = $this->_getinfo($img);
      if (FALSE == $imgInfo){
         return FALSE;
      }
      $imgType = $imgInfo['type'];
      switch ($imgType){ // 创建一个图，并给出扩展名
         case "jpg" :
            $img = imagecreatefromjpeg($img);
            $ext_name = 'jpg';
            break;
         case 'gif' :
            $img = imagecreatefromgif($img);
            $ext_name = 'gif';
            break;
         case 'png' :
            $img = imagecreatefrompng($img);
            $ext_name = 'png';
            break;
         default : // 如果类型错误，生成一张空白图
            $img = imagecreate($thumbWidth,$thumbHeight);
            imagecolorallocate($img,0x00,0x00,0x00);
            $ext_name = 'jpg';
      }
      // 缩放后的图片尺寸(小则拉伸，大就缩放)
      $imgWidth = $imgInfo['width'];
      $imgHeight = $imgInfo['height'];
      if ($imgHeight > $imgWidth){ // 竖图
         $newHeight = $thumbHeight;
         $newWidth = ceil($imgWidth / ($imgHeight / $thumbHeight ));
      }else if($imgHeight < $imgWidth){ // 横图
         $newHeight = ceil($imgHeight / ($imgWidth / $thumbWidth ));
         $newWidth = $thumbWidth;
      }else if($imgHeight == $imgWidth){ // 等比例图
         $newHeight = $thumbWidth;
         $newWidth = $thumbWidth;
      }
      // 生成图层
      $bgimg = imagecreatetruecolor($newWidth,$newHeight);
      $bg = imagecolorallocate($bgimg,0xFF,0xFF,0xFF);
      imagefill($bgimg,0,0,$bg);
      // 投影
      if (TRUE == $this->autoShandow){
         // 处理投影长度
         $this->shandowX = abs($this->shandowX);
         $this->shandowX = abs($this->shandowX);
         // 处理投影颜色
         $shandowColor = strtoupper($this->shondowColor);
         if (empty($shandowColor)){
            $shandowColor = '#CCCCCC';
         }else{
            $shandowColorValueLength = strlen($shandowColor);
            if (7 == $shandowColorValueLength || 6 == $shandowColorValueLength){
               $rgbB = substr($this->shondowColor,-2,2);
               $rgbG = substr($this->shondowColor,-4,2);
               $rgbR = substr($this->shondowColor,-6,2);
            }else if (4 == $shandowColorValueLength || 3 == $shandowColorValueLength){
               $rgbB = substr($this->shondowColor,-1,1);
               $rgbG = substr($this->shondowColor,-2,1);
               $rgbR = substr($this->shondowColor,-3,1);
               $rgbB .= $rgbB; // C->CC
               $rgbG .= $rgbG; // G->GG
               $rgbR .= $rgbR;
            }else{
               $rgbB = $rgbG = $rgbR = 'CC';
            }
         }
         $rgbR = hexdec($rgbR);
         $rgbG = hexdec($rgbG);
         $rgbB = hexdec($rgbB);
         // 生成投影
         $newWidth = $newWidth - $this->shandowX;
         $newHeight = $newHeight - $this->shandowY;
         $shandowImg = imagecreatetruecolor($newWidth,$newHeight);
         $shandowBg = imagecolorallocate($shandowImg,$rgbR,$rgbG,$rgbB);
         @imagefill($shandowImg,0,0,$shandowBg);
         $shandowXOffset = round($this->shandowX / 2); // 偏移后留出模糊区域
         $shandowYOffset = round($this->shandowY / 2);
         $sampled = imagecopy ($bgimg, $shandowImg, $shandowXOffset, $shandowYOffset, 0, 0, $newWidth, $newHeight);
         $shandow = imagefilter($bgimg,IMG_FILTER_GAUSSIAN_BLUR); // 用高斯算法模糊图像
         $shandow = imagefilter($bgimg,IMG_FILTER_SELECTIVE_BLUR); // 模糊图像
      }
      // 生成图片
      $sampled = imagecopyresampled($bgimg,$img,0,0,0,0,$newWidth,$newHeight,$imgWidth,$imgHeight);
 
      if(!$sampled ){
         $this->error = '缩略图生成失败';
         @unlink($this->uploadFolder . '/' . $filename); // 删除上传的图片
         return FALSE;
      }
      $filename = basename($filename);
      $newFileName = substr($filename, 0, strrpos($filename, ".")) . $thumbTail . '.' . $ext_name ; // 新名字
      $thumbPath = $this->thumbFolder . '/' . $newFileName;
      switch ($ext_name){
         case 'jpg':
            $result = imagejpeg($bgimg, $thumbPath);
            break;
         case 'gif':
            $result = imagegif($bgimg, $thumbPath);
            break;
         case 'png':
            $result = imagepng($bgimg, $thumbPath);
            break;
         default: // 上边判断类型出错时会创建一张空白图，并给出扩展名为jpg
            $result = imagejpeg($bgimg, $thumbPath);
      }
      if ($result){
         $this->thumbPath = $thumbPath;
         return $thumbPath;
      }else{
         $this->error = '缩略图创建失败';
         @unlink($this->uploadFolder . '/' . $filename); // 删除上传的图片
         return FALSE;
      }
   } // end thumb
 
   // 说明：创建文件存放目录
   private function _mkdir(){
      $baseDir = dirname($this->uploadFolder);
      if(!is_writable($baseDir)){
         $this->error = '基目录不可写';
         return FALSE;
      }
      // 创建文件上传目录
      if(!is_dir($this->uploadFolder)){
         $mkdirResutlt = @mkdir($this->uploadFolder);
         if (!$mkdirResutlt){
            $this->error = '存放图片的目录创建失败';
            return FALSE;
         }
      }
      // 创建缩略图目录
      if(!is_dir($this->thumbFolder) && TRUE == $this->autoThumb && 'image' == $this->uploadFileType){
         $mkdirResutlt = @mkdir($this->thumbFolder);
         if (!$mkdirResutlt){
            $this->error = '存放缩略图的目录创建失败';
            return FALSE;
         }
      }
      // 创建子目录
      if($this->autoSubfolder){
         $subFolder = date($this->SubfolderFormat);
         $newUploadFolder = $this->uploadFolder . '/' . $subFolder;
         if(!is_dir($newUploadFolder)){
            $mkdirResutlt = @mkdir($newUploadFolder);
            if(!$mkdirResutlt){
               $this->error = '下级目录创建失败';
               return FALSE;
            }
         }
         $newThumbFolder = $this->thumbFolder . '/' . $subFolder;
         if(!is_dir($newThumbFolder) && 'image' == $this->uploadFileType){
            $mkdirResutlt = @mkdir($newThumbFolder);
            if(!$mkdirResutlt){
               $this->error = '下级缩略图目录创建失败';
               return FALSE;
            }
         }
         $this->uploadFolder = $newUploadFolder;
         $this->thumbFolder = $newThumbFolder;
      }
   }
   // 调取图片的缩略图名字
   // 效果：/upload/20100506.jpg -> /upload/20100506_thumb.jpg
   public function getThumbPath($filename, $tail = '_thumb'){
      if(empty($filename)){
         return '';
      }
      $extstr=explode('.', $filename);
      if (2 <= count($extstr)){ // 有扩展名(可有多个，比如"filebasename.b.c.d.kkk")
         $count=count($extstr)-1;
      }else{
         return $filename . $tail; // 没有扩展名的话，直接加个"尾巴"后输出
      }
      $extname = $extstr[$count];
      $newname = substr($filename, 0, strrpos($filename, ".")) . $tail . "." . $extname;
      return str_replace($this->uploadFolder, $this->thumbFolder, $newname);
   }
}
?>